/*
 * Project: Sudoku Explainer
 * Copyright (C) 2006-2007 Nicolas Juillerat
 * 
 * Project: Sudoku Hint Engine for Android
 * Copyright (C) 2010-2013 Emanuel Moecklin
 * 
 * Available under the terms of the Lesser General Public License (LGPL)
 */
package com.onegravity.sudoku.solver.rules.uniqueness;

import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.onegravity.sudoku.solver.HintResource;
import com.onegravity.sudoku.solver.HintType;
import com.onegravity.sudoku.solver.R;
import com.onegravity.sudoku.solver.data.Cell;
import com.onegravity.sudoku.solver.data.ImmutableCell;
import com.onegravity.sudoku.solver.data.Region;
import com.onegravity.sudoku.solver.util.HtmlLoader;
import com.onegravity.sudoku.solver.util.ValuesFormatter;

public class UniquenessTestType3NakedHint extends UniquenessTestHint {

	private static final long serialVersionUID = -1411059090250667665L;
	
	private final Cell c1;
    private final Cell c2;
    private final int[] otherValues;
    private final Region region;
    private final Cell[] cells; // other cells of the naked set
    private final int[] nakedValues; // values of the naked set

    public UniquenessTestType3NakedHint(List<Cell> loop, int v1, int v2,
            Map<Cell, BitSet> removablePotentials, Cell c1, Cell c2, int[] otherValues,
            Region region, Cell[] cells, int[] values) {
        super(loop, v1, v2, removablePotentials);
        this.c1 = c1;
        this.c2 = c2;
        this.otherValues = otherValues;
        this.region = region;
        this.cells = cells;
        this.nakedValues = values;
    }

    @Override
    public double getDifficulty() {
        double toAdd = (nakedValues.length - 1) * 0.1; // Pair=0.1, Quad=0.3
        return super.getDifficulty() + toAdd;
    }

    @Override
    public Collection<Cell> getSelectedCells(HintType hintType) {
    	switch(hintType) {
    	case SMALL_CLUE: return super.getSelectedCells(hintType);
    	case HINT: 
    		// doesn't look good
    		// return Arrays.asList(new Cell[] {c1, c2}); 
    	case BIG_CLUE:
    	default: return null;
    	}
    }

    @Override
    public Map<Cell, BitSet> getSelectedPotentials(HintType hintType, int viewNum) {
        return (hintType!=HintType.SMALL_CLUE) ? super.getSelectedPotentials(hintType, viewNum) : null;
    }

    @Override
    public Map<ImmutableCell, BitSet> getRemovedPotentials(HintType hintType) {
        return (hintType==HintType.HINT) ? super.getRemovedPotentials(hintType) : null;
    }

    @Override
    public Region[] getRegions(HintType hintType) {
        return (hintType==HintType.HINT) ? new Region[] {region} : null;
    }

    @Override
    public Map<Cell, BitSet> getSecondarySelectedPotentials(HintType hintType, int viewNum) {
        Map<Cell, BitSet> result = new HashMap<Cell, BitSet>();
        appendOrangePotentials(result);
        return (hintType!=HintType.SMALL_CLUE) ? result : null;
    }

    private Map<Cell, BitSet> appendOrangePotentials(Map<Cell, BitSet> potentials) {
        BitSet nakedSet = new BitSet(10);
        for (int i = 0; i < nakedValues.length; i++){
            nakedSet.set(nakedValues[i]);
        }
        
        BitSet otherSet = new BitSet(10);
        for (int i = 0; i < otherValues.length; i++) {
            otherSet.set(otherValues[i]);
        }
        
        // collect naked subset potentials for cell outside the rectangle  
        for (Cell cell : cells) {
        	BitSet tmp = (BitSet) cell.getPotentialValues().clone();
        	tmp.and(nakedSet);
            potentials.put(cell, tmp);
        }

        // collect naked subset potentials for ceiling 1 cell  
        BitSet prevSet = potentials.get(c1);
        if (prevSet == null) prevSet = (BitSet) c1.getPotentialValues().clone();
        prevSet.and(otherSet);
        potentials.put(c1, prevSet);

        // collect naked subset potentials for ceiling 2 cell  
        prevSet = potentials.get(c2);
        if (prevSet == null) prevSet = (BitSet) c2.getPotentialValues().clone();
        prevSet.and(otherSet);
        potentials.put(c2, prevSet);
        
        return potentials;
    }

    @Override
    protected int getType() {
        return 3;
    }
    
    @Override
    protected HintResource getResource() {
        return HintResource.UNIQUENESS_TEST_3_NAKED;
    }

    protected String getAppendix() {
    	return " (" + HintResource.getString(R.string.hint_naked) + " " + HintResource.getString(setNamesRes[nakedValues.length - 2]) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }

    @Override
    public String getHintHtml(HintType hintType) {
        String andString = HintResource.getString(R.string.and);
        String orString = HintResource.getString(R.string.or);
        String result = HtmlLoader.loadHtml(HintResource.UNIQUENESS_TEST_3_NAKED.hint(hintType));
        String type = getTypeName();
        Cell[] loopCells = new Cell[loop.size()];
        loop.toArray(loopCells);
        String allCells = Cell.toString(loopCells);
        String cell1 = c1.toString();
        String cell2 = c2.toString();
        String valuesOrName = ValuesFormatter.formatValues(otherValues, " " + orString + " "); //$NON-NLS-1$ //$NON-NLS-2$
        String setName = HintResource.getString(setNamesRes[nakedValues.length - 2]);
        String otherCells = ValuesFormatter.formatCells(cells, " " + andString + " "); //$NON-NLS-1$ //$NON-NLS-2$
        String valuesAndName = ValuesFormatter.formatValues(nakedValues, " " + andString + " "); //$NON-NLS-1$ //$NON-NLS-2$
        String regionName = region.toFullString();
        result = HtmlLoader.format(result, type, v1, v2, allCells, cell1, cell2, valuesOrName, setName, otherCells, valuesAndName, regionName, loop.size());
        return result;
    }

    /**
     * Overriden to differentiate hints with different naked sets.
     * <p>
     * Because we only make different objects that are equal according
     * to <tt>super.equals()</tt>, <tt>hashCode()</tt> does not need
     * to be overriden.
     */
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof UniquenessTestType3NakedHint))
            return false;
        if (!super.equals(o))
            return false;
        UniquenessTestType3NakedHint other = (UniquenessTestType3NakedHint)o;
        if (!this.region.equals(other.region))
            return false;
        if (this.nakedValues.length != other.nakedValues.length)
            return false;
        for (int i = 0; i < nakedValues.length; i++) {
            if (this.nakedValues[i] != other.nakedValues[i])
                return false;
        }
        for (int i = 0; i < cells.length; i++) {
            if (!this.cells[i].equals(other.cells[i]))
                return false;
        }
        return true;
    }

}
